package com.zdft.promotion.common.util;

import com.alibaba.fastjson.JSON;
import com.zdft.promotion.RpcServiceException;
import com.zdft.promotion.common.service.BusinessException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.groups.Default;
import javax.validation.metadata.ConstraintDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Iterator;

/**
 * rpc服务异常处理器
 */
@Aspect
@Component
public class RpcExceptionHandler
{
    private static final Logger log = LoggerFactory.getLogger(RpcExceptionHandler.class);

    private final Validator validator;

    public RpcExceptionHandler()
    {
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    @Around("execution(public * com.zdft.promotion.*.rpc..*.*(..))")
    public Object handle(ProceedingJoinPoint point) throws Throwable
    {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Annotation[][] annotations = method.getParameterAnnotations();
        Object[] args = point.getArgs();
        String argJson = JSON.toJSONString(args);
        try
        {
            for (int i = 0; i < annotations.length; i++)
            {
                for (Annotation ann : annotations[i])
                {
                    if (ann.annotationType() == Valid.class)
                    {
                        valid(args[i]);
                    }
                }
            }

            log.info("Enter method:{}...", signature);
            log.debug("Parameters:{}.", argJson);
            Object object = point.proceed();
            log.debug("Return:{}", object);
            return object;
        }
        catch (InvocationTargetException e)
        {
            doException(argJson, e.getTargetException());
        }
        catch (UndeclaredThrowableException e)
        {
            doException(argJson, e.getUndeclaredThrowable());
        }
        catch (Throwable throwable)
        {
            doException(argJson, throwable);
        }

        return null;
    }

    /**
     * 校验对象
     *
     * @param obj 对象
     */
    private void valid(Object obj)
    {
        Iterator<ConstraintViolation<Object>> iterator = validator.validate(obj, Default.class).iterator();
        if (iterator.hasNext())
        {
            ConstraintViolation<Object> v = iterator.next();
            ConstraintDescriptor<?> desc = v.getConstraintDescriptor();
            String code = "err_" + v.getPropertyPath() + "_" + desc.getAnnotation().annotationType().getSimpleName();
            String message = desc.getMessageTemplate();
            throw new RpcServiceException(code, message);
        }
    }

    /**
     * 处理异常
     *
     * @param argJson   入参
     * @param throwable 异常
     */
    private void doException(String argJson, Throwable throwable)
    {
        if (throwable instanceof RpcServiceException)
        {
            log.error("Parameters:{}\nException:{}.", argJson, throwable.toString());
            throw (RpcServiceException) throwable;
        }
        if (throwable instanceof BusinessException)
        {
            log.error("Parameters:{}\nException:{}.", argJson, throwable.toString());
            BusinessException e = (BusinessException) throwable;
            throw new RpcServiceException(e.getCode(), e.getMessage());
        }
        log.error("Parameters:{}\nException:{}.", argJson, ExceptionUtil.toString(throwable));
        throw new RpcServiceException("SYS_FAULT", "内部异常，请联系管理员。");
    }
}
